﻿namespace Monadic.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Diagnostics;

    [TestClass]
    public class FunctiveTests
    {
        Func<int, int> square;
        Func<int, double> squareRoot;
        Func<int, int> identity;
        Func<int, dynamic> factorial;


        public FunctiveTests()
        {
            square = x => x * x;
            squareRoot = x => Math.Sqrt(x);
            identity = x => x;
            factorial = from x in identity.Sum()
                        where x != 0
                        select x ?? 1;
        }

        [TestMethod]
        public void Projection0()
        {
            Func<int> f = () => 2;

            var test = from x in f
                       select x * 2;

            Assert.AreEqual(4, test());
        }
        
        [TestMethod]
        public void Projection1()
        {
            Func<int, int> f = x => x + 2;

            var test = from x in f
                       select x * 2;

            Assert.AreEqual(6, test(1));
        }

        [TestMethod]
        public void ComplexProjection()
        {
            Func<double, int> f = x => (int)Math.Ceiling(x);

            var test = from x in f
                       select x * 2;

            Assert.AreEqual(4, test(1.1));
        }

        [TestMethod]
        public void Hypoteneuse()
        {
            var hypoteneuse = from h in square
                              from w in square
                              select Math.Sqrt(h + w);

            Assert.AreEqual(5, hypoteneuse(3, 4));
        }

        [TestMethod]
        public void HypoteneuseByExtension()
        {
            var hypoteneuse = square.SelectMany(square, (h, w) => Math.Sqrt(h + w));

            Assert.AreEqual(5, hypoteneuse(3, 4));
        }

        [TestMethod]
        public void SimpleSummation()
        {
            Func<int, int> f = x => x;
            var g = f.Sum();

            Assert.AreEqual(6, g(3));
        }

        [TestMethod]
        public void Pi()
        {
            Func<Int32, Double> f = k =>  4 * (Math.Pow(-1, k) / ((2.0 * k) + 1));
            var calculatePi = f.Sum();
            Assert.AreEqual(3.14159, calculatePi(200000), 7);
        }

        [TestMethod]
        public void Iterate()
        {
            Func<Int32, Int32> f = x => x + 1;
            var nums = f.Iterate(1).Take(10);
            Assert.AreEqual(10, nums.Last());
        }

        // The test works.. how do you automate it?
        [TestMethod, Ignore]
        public void IterateShouldNotStackOverflow()
        {
            Func<Int32, Int32> f = x => x;
            foreach (var n in f.Iterate(1))
            {
            }
        }

        [TestMethod]
        public void Flip()
        {
            Func<double, double, double> f = (x, y) => x / y;
            var g = f.Flip();
            Assert.AreEqual(2, f(2, 1), 1);
            Assert.AreEqual(0.5, g(2, 1), 2);
        }
    }
}
